package com.xsq.wifi.utils;

import android.util.Log;
import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.*;

public class PCPLongLiveClient {
    private static final String TAG = "PCPLongLiveClient";
    private static final int HEADER_SIZE = 8;
    private static final int MAX_PACKET_SIZE = 1440 * 2; // 分块大小
    private static final int HEARTBEAT_INTERVAL = 10; // 心跳间隔(秒)

    // 连接参数
    private final String destIp;
    private final int destPort;
    private final byte version;
    private final int magicNum;

    // 连接状态
    private volatile Socket socket;
    private volatile OutputStream outputStream;
    private volatile InputStream inputStream;
    private volatile boolean isRunning = false;
    private final Object connectionLock = new Object();

    // 线程池
    private final ExecutorService workerPool = Executors.newCachedThreadPool();
    private ScheduledExecutorService heartbeatScheduler;

    public interface PCPCallback {
        void onProgress(int progress); // 传输进度 0-100
        void onSuccess(byte[] response); // 成功响应
        void onFailure(String error);   // 错误信息
    }

    public PCPLongLiveClient(String ip, int port, int magicNum, byte version) {
        this.destIp = ip;
        this.destPort = port;
        this.magicNum = magicNum;
        this.version = version;
    }

    //=============== 公开方法 ===============//
    public void start() throws IOException {
        synchronized (connectionLock) {
            if (isRunning) return;
            connect();
            startHeartbeat();
            isRunning = true;
            Log.i(TAG, "长连接已启动");
        }
    }

    public void stop() {
        synchronized (connectionLock) {
            if (!isRunning) return;
            isRunning = false;
            stopHeartbeat();
            disconnect();
            workerPool.shutdown();
            Log.i(TAG, "长连接已关闭");
        }
    }

    /**
     * 发送文件（单指令头+流式数据）
     * @param file 待发送文件
     * @param callback 进度/结果回调
     */
    public void sendFile(File file, PCPCallback callback) {
        workerPool.execute(() -> {
            try {
                // 1. 发送指令头 (0x31)
                sendFileHeader(file);

                // 2. 流式发送文件内容（纯数据，无协议头）
                streamFileData(file, callback);

                // 3. 接收最终响应
                byte[] response = receiveData();
                callback.onSuccess(response);

            } catch (IOException e) {
                Log.e(TAG, "文件传输失败", e);
                callback.onFailure(e.getMessage());
                tryReconnect();
            }
        });
    }

    //=============== 核心私有方法 ===============//
    private void connect() throws IOException {
        disconnect(); // 确保先关闭旧连接

        socket = new Socket();
        socket.setKeepAlive(true);
        socket.connect(new java.net.InetSocketAddress(destIp, destPort), 30000);
        outputStream = socket.getOutputStream();
        inputStream = socket.getInputStream();

        // 握手验证
        sendDataInternal((byte)0xFF, magicNum, "READY".getBytes());
        byte[] ack = receiveData();
        if (ack == null || !"OK".equals(new String(ack))) {
            throw new IOException("服务器握手失败");
        }
        Log.d(TAG, "连接已验证");
    }

    private void disconnect() {
        try {
            if (outputStream != null) outputStream.close();
            if (inputStream != null) inputStream.close();
            if (socket != null) socket.close();
        } catch (IOException e) {
            Log.w(TAG, "关闭连接异常", e);
        } finally {
            outputStream = null;
            inputStream = null;
            socket = null;
        }
    }

    private void sendFileHeader(File file) throws IOException {
        ByteArrayOutputStream headerData = new ByteArrayOutputStream();
        headerData.write(file.getName().getBytes("UTF-8"));
        headerData.write(0x00); // 分隔符
        headerData.write(String.valueOf(file.length()).getBytes());

        sendDataInternal((byte)0x31, calculateChecksum(file), headerData.toByteArray());
        receiveData(); // 等待服务器确认
    }

    private void streamFileData(File file, PCPCallback callback) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[MAX_PACKET_SIZE];
            long totalSent = 0;
            long fileSize = file.length();

            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                synchronized (connectionLock) {
                    if (!isConnected()) throw new IOException("连接已断开");
                    outputStream.write(buffer, 0, bytesRead); // 直接写原始数据
                    outputStream.flush();
                }

                totalSent += bytesRead;
                callback.onProgress((int) (totalSent * 100 / fileSize));
            }
        }
    }

    //=============== 协议相关方法 ===============//
    private void sendDataInternal(byte cmd, int value, byte[] data) throws IOException {
        ByteBuffer header = ByteBuffer.allocate(HEADER_SIZE)
                .order(ByteOrder.LITTLE_ENDIAN)
                .put(version).put(cmd)
                .putShort((short)value)
                .putInt(data != null ? data.length : 0);

        synchronized (connectionLock) {
            outputStream.write(header.array());
            if (data != null) outputStream.write(data);
            outputStream.flush();
        }
    }

    private byte[] receiveData() throws IOException {
        byte[] headerBytes = new byte[HEADER_SIZE];
        readFully(inputStream, headerBytes);

        ByteBuffer header = ByteBuffer.wrap(headerBytes).order(ByteOrder.LITTLE_ENDIAN);
        int dataLength = header.getInt(4); // 读取长度字段

        if (dataLength > 0) {
            byte[] data = new byte[dataLength];
            readFully(inputStream, data);
            return data;
        }
        return null;
    }

    //=============== 辅助方法 ===============//
    private void readFully(InputStream in, byte[] buffer) throws IOException {
        int read = 0;
        while (read < buffer.length) {
            int count = in.read(buffer, read, buffer.length - read);
            if (count == -1) throw new EOFException("流意外结束");
            read += count;
        }
    }

    private int calculateChecksum(File file) throws IOException {
        int sum = 0;
        try (InputStream is = new FileInputStream(file)) {
            byte[] buf = new byte[8192];
            int len;
            while ((len = is.read(buf)) > 0) {
                for (int i = 0; i < len; i++) {
                    sum += buf[i] & 0xFF;
                }
            }
        }
        return sum & 0xFFFF;
    }

    private void startHeartbeat() {
        heartbeatScheduler = Executors.newSingleThreadScheduledExecutor();
        heartbeatScheduler.scheduleAtFixedRate(() -> {
            if (!isRunning) return;

            try {
                synchronized (connectionLock) {
                    if (!isConnected()) return;
                    sendDataInternal((byte)0x00, 0, null); // 心跳包
                    byte[] ack = receiveData(); // 必须收到响应
                    if (ack == null) throw new IOException("心跳无响应");
                }
            } catch (Exception e) {
                Log.w(TAG, "心跳异常", e);
                tryReconnect();
            }
        }, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
    }

    private void stopHeartbeat() {
        if (heartbeatScheduler != null) {
            heartbeatScheduler.shutdownNow();
        }
    }

    private void tryReconnect() {
        if (!isRunning) return;

        synchronized (connectionLock) {
            try {
                Log.w(TAG, "尝试重新连接...");
                disconnect();
                Thread.sleep(1000);
                connect();
            } catch (Exception e) {
                Log.e(TAG, "重连失败", e);
                stop();
            }
        }
    }

    private boolean isConnected() {
        return socket != null && socket.isConnected() && !socket.isClosed();
    }
}